﻿using MongoDB.Driver;
using MongoDB.Driver.Linq;
using Sustainalytics.DataLayer;
using Sustainalytics.Entities.ESGRatings;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Sustainalytics.ClientDW.DataAccessLayer.ESGRatings
{
    public class ReferenceUniversesRepository : IReferenceUniversesRepository
    {
        private IMongoDatabase _database;
        private string _databaseName;

        private ReferenceUniversesRepository(string connectionString)
        {
            var dbConnectionString = MongoUrl.Create(connectionString);

            if (string.IsNullOrWhiteSpace(dbConnectionString.DatabaseName))
            {
                throw new ArgumentException("No database specified in the connection string!");
            }

            var client = new MongoClient(dbConnectionString);
            _database = client.GetDatabase(dbConnectionString.DatabaseName);
            _databaseName = dbConnectionString.DatabaseName;
        }

        public static IReferenceUniversesRepository GetInstance(string connectionString)
        {
            return new ReferenceUniversesRepository(connectionString);
        }

        public void Cleanup()
        {
            _database.Client.DropDatabase(_databaseName);
        }

        public ReferenceUniverse GetReferenceUniverse(Guid referenceUniverseId)
        {
            return _database.GetCollection<ReferenceUniverse>(typeof(ReferenceUniverse).Name)
                                        .AsQueryable()
                                        .SingleOrDefault(r => r.Id == referenceUniverseId);
        }

        public ReferenceUniverse GetReferenceUniverseByName(string referenceUniverseName)
        {
            if (string.IsNullOrWhiteSpace(referenceUniverseName))
            {
                throw new ArgumentException("No reference universe name provided for searching!");
            }

            return _database.GetCollection<ReferenceUniverse>(typeof(ReferenceUniverse).Name)
                            .AsQueryable()
                            .SingleOrDefault(r => r.Name.ToLowerInvariant() == referenceUniverseName.ToLowerInvariant());
        }

        public void SaveReferenceUniverse(ReferenceUniverse referenceUniverse)
        {
            if (referenceUniverse == null)
            {
                throw new ArgumentException("No reference universe provided for saving!");
            }

            _database.GetCollection<ReferenceUniverse>(typeof(ReferenceUniverse).Name).FindOneAndReplace(
                Builders<ReferenceUniverse>.Filter.Eq(x=> x.Id, referenceUniverse.Id),
                referenceUniverse);
        }

        public void UpdateReferenceUniverse(ReferenceUniverse referenceUniverse)
        {
            throw new NotImplementedException();
        }

        public void DeleteReferenceUniverse(Guid referenceUniverseId)
        {
            throw new NotImplementedException();
        }

        public List<ReferenceUniverse> ListReferenceUniverses(bool includeReferenceUniversesDetails)
        {
            var refUnivs = _database.GetCollectionByNameType<ReferenceUniverse>();
            if (includeReferenceUniversesDetails)
            {
                return refUnivs.Find(_ => true).ToList();
            }

            return refUnivs
                .Find(_ => true)
                .Project<ReferenceUniverse>(Builders<ReferenceUniverse>.Projection
                    .Include(x=> x.Id)
                    .Include(x=> x.Name))
                .SortBy(a => a.Name)
                .ToList();
        }
    }
}
